﻿using System.Runtime.InteropServices;
using OnDash.Mathematics.Geometry;
using OnDash.Windowing.Common;
using OnDash.Windowing.Common.Events;
using OnDash.Windowing.GraphicsLibraryFramework;
using OnDash.Windowing.GraphicsLibraryFramework.Enums;
using Monitor = OnDash.Windowing.GraphicsLibraryFramework.Monitor;

namespace OnDash.Windowing.Desktop;

public struct Monitors
{
    static unsafe Monitors()
    {
        GlfwProvider.EnsureInitialized();

        _monitorCallback = MonitorCallback;
        Glfw.SetMonitorCallback(_monitorCallback);
    }

    private static readonly GlfwCallbacks.MonitorCallback _monitorCallback;
    public static event Action<MonitorEventArgs> OnMonitorConnected;

    [Obsolete("Use GetMonitors() instead.", true)]
    public static unsafe int Count => throw new Exception("This property is Obsolete, use GetMonitors() instead.");

    public static float GetPlatformDefaultDpi() =>
        RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 72f : 96f;

    private static int GetRectangleIntersectionArea(Box2 a, Box2 b)
    {
        var area = Box2.Intersect(a, b);
        return (int)(area.Size.X * area.Size.Y);
    }

    public static unsafe MonitorInfo GetPrimaryMonitor()
    {
        var monitor = new MonitorHandle((IntPtr)Glfw.GetPrimaryMonitor());
        return new MonitorInfo(monitor);
    }

    public static unsafe List<MonitorInfo> GetMonitors()
    {
        var monitorsRaw = Glfw.GetMonitorsRaw(out var count);

        var monitors = new List<MonitorInfo>(count);

        for (var i = 0; i < count; i++)
            monitors.Add(new MonitorInfo(new MonitorHandle((IntPtr)monitorsRaw[i])));

        return monitors;
    }

    public static unsafe MonitorInfo GetMonitorFromWindow(Window* window)
    {
        var value = new MonitorHandle((IntPtr)Glfw.GetWindowMonitor(window));
        if (value.Pointer != IntPtr.Zero) return new MonitorInfo(value);

        Box2 windowArea;
        {
            Glfw.GetWindowPos(window, out var windowX, out var windowY);
            Glfw.GetWindowSize(window, out var windowWidth, out var windowHeight);
            windowArea = new Box2(windowX, windowY, windowX + windowWidth, windowY + windowHeight);
        }

        var monitors = GetMonitors();

        var selectedIndex = 0;
        for (var i = 0; i < monitors.Count; i++)
        {
            if (
                GetRectangleIntersectionArea(monitors[i].ClientArea, windowArea) >
                GetRectangleIntersectionArea(monitors[selectedIndex].ClientArea, windowArea)
            )
            {
                selectedIndex = i;
            }
        }

        return monitors[selectedIndex];
    }

    public static unsafe MonitorInfo GetMonitorFromWindow(NativeWindow window) =>
        GetMonitorFromWindow(window.WindowPtr);

    private static unsafe void MonitorCallback(Monitor* monitor, ConnectedState state)
    {
        OnMonitorConnected.Invoke(new MonitorEventArgs(new MonitorHandle((IntPtr)monitor),
            state == ConnectedState.Connected));
    }

    [Obsolete("Use GetMonitors() instead.", true)]
    public static bool TryGetMonitorInfo(int index, out MonitorInfo info) =>
        throw new Exception("This function is Obsolete, use GetMonitors() instead.");

    [Obsolete("Use GetMonitors() instead.", true)]
    public static bool TryGetMonitorInfo(MonitorHandle monitor, out MonitorInfo info) =>
        throw new Exception("This function is Obsolete, use GetMonitors() instead.");

    [Obsolete("There is no cache anymore", true)]
    public static bool CheckCache() =>
        throw new Exception("There is no cache anymore, don't call this function.");

    [Obsolete("There is no cache anymore", true)]
    public static void BuildMonitorCache() =>
        throw new Exception("There is no cache anymore, don't call this function.");
}